package cm

import (
	"context"
	"fmt"
	"math/big"
	"strconv"
	"time"

	"gitee.com/law-api/cm/client"

	"github.com/PicoChain/accounts/abi/bind"
	"github.com/PicoChain/common"
	"github.com/PicoChain/crypto"
	"github.com/PicoChain/types"
)

//transfer give a new account 10 eth for operating in the picoChain.
func transfer(sender *bind.TransactOpts, to common.Address, amount *big.Int, nonce uint64, c *client.Client) (*types.Transaction, error) {
	// Figure out the gas allowance and gas price values
	gasPrice := new(big.Int).SetUint64(1e9)

	//If the contract surely has code (or code is not needed), estimate the transaction
	gasLimit := uint64(21000)

	// Create the transaction, sign it and schedule it for execution
	var rawTx *types.Transaction
	rawTx = types.NewTransaction(nonce, to, amount, gasLimit, gasPrice, nil)

	signedTx, err := sender.Signer(types.HomesteadSigner{}, sender.From, rawTx)
	if err != nil {
		return nil, fmt.Errorf("signerTxs failed: %v", err)
	}
	if err := c.SendTransaction(context.TODO(), signedTx); err != nil {
		return nil, fmt.Errorf("sendTransaction failed: %v", err)
	}
	return signedTx, nil
}

//waitMined start a rountine to listen a transaction whether be blocked.
func (cm *ClientManager) waitMined(c *client.Client, tx *types.Transaction, timeout time.Duration) error {
	//set timeout
	ctx, cancel := context.WithTimeout(context.TODO(), timeout)
	defer cancel()
	//watch transaction status
	_, err := bind.WaitMined(ctx, c, tx)
	return err
}

//getBlockByTxHash look up the block where the transaction places.
func (cm *ClientManager) getBlockByTxHash(c *client.Client, txHash common.Hash) (*types.Block, error) {
	//read transaction status
	blockNumber, blockHash, err := c.GetBlockNumberAndHashByTx(context.TODO(), txHash)
	if err != nil || blockHash == (common.Hash{}) {
		return nil, err
	}

	//request by hash.
	block, err := c.BlockByHash(context.TODO(), blockHash)
	if err == nil {
		return block, nil
	}

	//request by number
	number, err := strconv.ParseUint(blockNumber, 0, 0)
	if err != nil {
		return nil, err
	}
	return c.BlockByNumber(context.TODO(), new(big.Int).SetUint64(number))
}

//getTransactionByHash look up the raw transaction by its hash
func (cm *ClientManager) getTransactionByHash(c *client.Client, txHash common.Hash) (*types.Transaction, error) {
	//request tx by hash
	tx, pending, err := c.TransactionByHash(context.TODO(), txHash)
	if pending == true {
		return tx, ErrTxPending
	}

	return tx, err
}

//getReceiptByTxHash look up the receipt when finish executing its transaction
func (cm *ClientManager) getReceiptByTxHash(txhash common.Hash) (*types.Receipt, error) {
	_, c := cm.randomClient()
	return c.TransactionReceipt(context.TODO(), txhash)
}

//checkContractStatus check the contract transaction whether execute successfully.
func (cm *ClientManager) checkContractStatus(c *client.Client, txHash common.Hash) (bool, error) {
	reciept, err := c.TransactionReceipt(context.TODO(), txHash)
	if err != nil {
		return false, err
	}
	return reciept.Status == uint64(1), nil
}

//CheckPrivateKey check the validity in this key symbol.
func (cm *ClientManager) CheckPrivateKey(key string) bool {
	//check key
	if _, err := crypto.HexToECDSA(key); err != nil {
		return false
	}
	return true
}
